package com.davidlife.javaee.redis.cases;

import redis.clients.jedis.Jedis;

import java.util.ArrayList;
import java.util.Set;

/*
因为存储会话数据随着时间的推移所需的内存会不断增加，需要定期清理旧的会话数据
为了限制会话数据的数量，这里决定只保存最新的1000万个会话。

在哪里执行清理：
    可以以守护进程的方式，
    也可以作为定期作业每隔一段时间执行一次
    还可以在每次执行某个操作时执行一次
 */
public class CleanSessionsThread extends Thread
{
    private Jedis conn;
    private int limit;
    private boolean quit;

    public CleanSessionsThread(int limit) {
        this.conn = new Jedis("localhost",6397);
//        this.conn.select(15);
        this.limit = limit;
    }

    public void quit() {
        quit = true;
    }

    public void run() {
        while (!quit) {
            long size = conn.zcard("recent:");
            if (size <= limit){
                try {
                    sleep(1000);
                }catch(InterruptedException ie){
                    Thread.currentThread().interrupt();
                }
                continue;
            }

            //移除最多100个旧的令牌
            long endIndex = Math.min(size - limit, 100);
            Set<String> tokenSet = conn.zrange("recent:", 0, endIndex - 1);
            String[] tokens = tokenSet.toArray(new String[tokenSet.size()]);

            ArrayList<String> sessionKeys = new ArrayList<String>();
            for (String token : tokens) {
                sessionKeys.add("viewed:" + token);
            }

            //移除相关的那些最旧的令牌
            conn.del(sessionKeys.toArray(new String[sessionKeys.size()]));
            conn.hdel("login:", tokens);
            conn.zrem("recent:", tokens);

            //注意：这里实际上有竞态条件（race condition）的存在，如果清理函数正在删除某个用户信息，而这个用户又在同一时间访问网站的话，
            //那么竞态条件就会导致用户的信息被错误的删除，当然这里不是很严重，只会导致用户需重新登录一次。
            //后面会讲解怎样防止类似的竞态条件的发生
        }
    }
}
